Libraries

Load data

I am using the AHA dataset for writing my code because it is the simplest dataset I can work with. Simplest = significant number of baseline samples without issue of repeated measures, balanced distribution of classes, likely difference to be detected present.

a_ps = readRDS("/Users/aa370/Library/CloudStorage/Box-Box/project_davidlab/LAD_LAB_Personnel/Ammara_A/Projects/AHA/220714_MN00462_0226_A000H3WKM5/20221110_results/ps_objects/AHA_trnl_with_metadata_20221205.rds")

a_ps
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 235 taxa and 95 samples ]
sample_data() Sample Data:       [ 95 samples by 33 sample variables ]
tax_table()   Taxonomy Table:    [ 235 taxa by 10 taxonomic ranks ]
c_ps = readRDS("/Users/aa370/Library/CloudStorage/Box-Box/project_davidlab/LAD_LAB_Personnel/Ammara_A/Projects/CHOICE/CHOICE_Trnl/CHOICE_20220912/ps_objects/choice_complete_20221205_clean.rds")

c_ps
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 571 taxa and 171 samples ]
sample_data() Sample Data:       [ 171 samples by 30 sample variables ]
tax_table()   Taxonomy Table:    [ 571 taxa by 10 taxonomic ranks ]

Preprocess

Remove problematic samples

Taking out samples that are not listed to have completed the study and samples with more than 0 reads reduces samples from 95 down to 77.

Taking only the baseline timepoint brings further down to 44 samples

Keeping non-completed samples reduced the AUC of modelling substantially, remove these samples as before.

a_ps = a_ps %>%
  subset_samples(completion == "Completed" & reads >0) %>%
  subset_samples(timepoint == "Baseline")

a_ps
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 235 taxa and 44 samples ]
sample_data() Sample Data:       [ 44 samples by 33 sample variables ]
tax_table()   Taxonomy Table:    [ 235 taxa by 10 taxonomic ranks ]

Class distribution in this dataset: Case = 20 Control = 24

sample_data(a_ps) %>%
  as.data.frame() %>%
  as_tibble() %>%
  group_by(group) %>%
  summarise(count = n())

Remove unassigned taxa

Debating whether this should be done for machine learning. Because we are simply throwing out because we don’t know what there are and even if they account for a small percentage of reads, they might still have important information.

I think I will refrain from removing unless I have a good reason to.

Don’t do this because some very important taxa for ML model turned out to be unassigned. - 12/8/22

Removing completely unassigned taxa reduces taxa from 235 to 82.

a_ps = a_ps %>%
  subset_taxa(is.na(superkingdom) == FALSE)

a_ps
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 82 taxa and 44 samples ]
sample_data() Sample Data:       [ 44 samples by 33 sample variables ]
tax_table()   Taxonomy Table:    [ 82 taxa by 10 taxonomic ranks ]

Merge phyloseq

combined = merge_phyloseq(c_ps, a_ps)

combined
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 674 taxa and 215 samples ]
sample_data() Sample Data:       [ 215 samples by 41 sample variables ]
tax_table()   Taxonomy Table:    [ 674 taxa by 10 taxonomic ranks ]

Aligning seq table

seqtab = combined@otu_table

seqtab_merged = dada2::collapseNoMismatch(seqtab)

combined = phyloseq(otu_table(seqtab_merged, taxa_are_rows=FALSE),
               sample_data(combined@sam_data), 
               tax_table(combined@tax_table))

combined
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 660 taxa and 215 samples ]
sample_data() Sample Data:       [ 215 samples by 41 sample variables ]
tax_table()   Taxonomy Table:    [ 660 taxa by 10 taxonomic ranks ]
sample_data(combined)
Sample Data:        [215 samples by 41 sample variables]:

Data transform

otu_clr = abundances(combined, "clr") %>% 
  t()

combined = phyloseq(otu_table(otu_clr, taxa_are_rows = FALSE),
                   sample_data(combined@sam_data),
                   tax_table(combined@tax_table))

rm(otu_clr)

Feature table

Extract data

Order of asv colnames in OTU table and tax table is the same.

features = combined@otu_table %>%
  as.data.frame() %>%
  as_tibble(rownames = NA) %>%
  rownames_to_column("sample")

feature_labels = c("sample")

features

Zero variance features

No features have zero variance

nzv = features %>%
  select(!sample) %>%
  nearZeroVar(saveMetrics = TRUE)
nzv %>%
  filter(nzv == TRUE)
rm(nzv)

Correlated predictors

summary(feature_cor[upper.tri(feature_cor)])
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-0.51377 -0.01487  0.03831  0.14088  0.18607  1.00000 
high_cor = findCorrelation(feature_cor, cutoff = .50) #using a more permissive cutoff because a lot of columns still left in the input table

filtered_features = features[,-high_cor]
rm(high_cor)
rm(feature_cor)

Linear dependencies

Input table

Modeling

Baseline performance

label_list = c("group", "group_shuffle")
results = loop_label(iterations = 20, input, label_list, percent = 0.8)

auc_df = results[[2]]

importance_df = results[[1]]

Loop

auc_list
 [1] 0.75 0.25 0.50 0.44 0.69 0.69 0.75 0.75 0.56 0.56 0.88 0.81 0.94
[14] 0.62 0.62 0.88 0.50 0.62 0.50 0.94

Plotting

AUC plot and t-test

Save external predictions

predictions

save = cummulative_external %>%
  #filter(auc == max) %>%
  left_join(samdf) %>%
  arrange(iteration, treatment, id, true_week)
Joining, by = "sample"

variable importances

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBMaWJyYXJpZXMKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KG1pY3JvYmlvbWUpCmxpYnJhcnkocGh5bG9zZXEpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KE1CdXRpbHMpCmBgYAoKIyBMb2FkIGRhdGEKCkkgYW0gdXNpbmcgdGhlIEFIQSBkYXRhc2V0IGZvciB3cml0aW5nIG15IGNvZGUgYmVjYXVzZSBpdCBpcyB0aGUgc2ltcGxlc3QgZGF0YXNldCBJIGNhbiB3b3JrIHdpdGguClNpbXBsZXN0ID0gc2lnbmlmaWNhbnQgbnVtYmVyIG9mIGJhc2VsaW5lIHNhbXBsZXMgd2l0aG91dCBpc3N1ZSBvZiByZXBlYXRlZCBtZWFzdXJlcywgYmFsYW5jZWQgZGlzdHJpYnV0aW9uIG9mIGNsYXNzZXMsIGxpa2VseSBkaWZmZXJlbmNlIHRvIGJlIGRldGVjdGVkIHByZXNlbnQuCgoKYGBge3J9CmFfcHMgPSByZWFkUkRTKCIvVXNlcnMvYWEzNzAvTGlicmFyeS9DbG91ZFN0b3JhZ2UvQm94LUJveC9wcm9qZWN0X2RhdmlkbGFiL0xBRF9MQUJfUGVyc29ubmVsL0FtbWFyYV9BL1Byb2plY3RzL0FIQS8yMjA3MTRfTU4wMDQ2Ml8wMjI2X0EwMDBIM1dLTTUvMjAyMjExMTBfcmVzdWx0cy9wc19vYmplY3RzL0FIQV90cm5sX3dpdGhfbWV0YWRhdGFfMjAyMjEyMDUucmRzIikKCmFfcHMKYGBgCgpgYGB7cn0KY19wcyA9IHJlYWRSRFMoIi9Vc2Vycy9hYTM3MC9MaWJyYXJ5L0Nsb3VkU3RvcmFnZS9Cb3gtQm94L3Byb2plY3RfZGF2aWRsYWIvTEFEX0xBQl9QZXJzb25uZWwvQW1tYXJhX0EvUHJvamVjdHMvQ0hPSUNFL0NIT0lDRV9Ucm5sL0NIT0lDRV8yMDIyMDkxMi9wc19vYmplY3RzL2Nob2ljZV9jb21wbGV0ZV8yMDIyMTIwNV9jbGVhbi5yZHMiKQoKY19wcwpgYGAKCiMgUHJlcHJvY2VzcwoKIyMgUmVtb3ZlIHByb2JsZW1hdGljIHNhbXBsZXMKClRha2luZyBvdXQgc2FtcGxlcyB0aGF0IGFyZSBub3QgbGlzdGVkIHRvIGhhdmUgY29tcGxldGVkIHRoZSBzdHVkeSBhbmQgc2FtcGxlcyB3aXRoIG1vcmUgdGhhbiAwIHJlYWRzIHJlZHVjZXMgc2FtcGxlcyBmcm9tIDk1IGRvd24gdG8gNzcuCgpUYWtpbmcgb25seSB0aGUgYmFzZWxpbmUgdGltZXBvaW50IGJyaW5ncyBmdXJ0aGVyIGRvd24gdG8gNDQgc2FtcGxlcwoKS2VlcGluZyBub24tY29tcGxldGVkIHNhbXBsZXMgcmVkdWNlZCB0aGUgQVVDIG9mIG1vZGVsbGluZyBzdWJzdGFudGlhbGx5LCByZW1vdmUgdGhlc2Ugc2FtcGxlcyBhcyBiZWZvcmUuCgpgYGB7cn0KYV9wcyA9IGFfcHMgJT4lCiAgc3Vic2V0X3NhbXBsZXMoY29tcGxldGlvbiA9PSAiQ29tcGxldGVkIiAmIHJlYWRzID4wKSAlPiUKICBzdWJzZXRfc2FtcGxlcyh0aW1lcG9pbnQgPT0gIkJhc2VsaW5lIikKCmFfcHMKCmBgYAoKQ2xhc3MgZGlzdHJpYnV0aW9uIGluIHRoaXMgZGF0YXNldDoKQ2FzZSA9IDIwCkNvbnRyb2wgPSAyNAoKYGBge3J9CnNhbXBsZV9kYXRhKGFfcHMpICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBhc190aWJibGUoKSAlPiUKICBncm91cF9ieShncm91cCkgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQpgYGAKIyMgUmVtb3ZlIHVuYXNzaWduZWQgdGF4YQoKRGViYXRpbmcgd2hldGhlciB0aGlzIHNob3VsZCBiZSBkb25lIGZvciBtYWNoaW5lIGxlYXJuaW5nLiBCZWNhdXNlIHdlIGFyZSBzaW1wbHkgdGhyb3dpbmcgb3V0IGJlY2F1c2Ugd2UgZG9uJ3Qga25vdyB3aGF0IHRoZXJlIGFyZSBhbmQgZXZlbiBpZiB0aGV5IGFjY291bnQgZm9yIGEgc21hbGwgcGVyY2VudGFnZSBvZiByZWFkcywgdGhleSBtaWdodCBzdGlsbCBoYXZlIGltcG9ydGFudCBpbmZvcm1hdGlvbi4KCkkgdGhpbmsgSSB3aWxsIHJlZnJhaW4gZnJvbSByZW1vdmluZyB1bmxlc3MgSSBoYXZlIGEgZ29vZCByZWFzb24gdG8uCgpEb24ndCBkbyB0aGlzIGJlY2F1c2Ugc29tZSB2ZXJ5IGltcG9ydGFudCB0YXhhIGZvciBNTCBtb2RlbCB0dXJuZWQgb3V0IHRvIGJlIHVuYXNzaWduZWQuIC0gMTIvOC8yMgoKUmVtb3ZpbmcgY29tcGxldGVseSB1bmFzc2lnbmVkIHRheGEgcmVkdWNlcyB0YXhhIGZyb20gMjM1IHRvIDgyLgoKYGBge3J9CiMgYV9wcyA9IGFfcHMgJT4lCiMgICBzdWJzZXRfdGF4YShpcy5uYShzdXBlcmtpbmdkb20pID09IEZBTFNFKQojIAojIGFfcHMKYGBgCgojIyBNZXJnZSBwaHlsb3NlcQoKYGBge3J9CmNvbWJpbmVkID0gbWVyZ2VfcGh5bG9zZXEoY19wcywgYV9wcykKCmNvbWJpbmVkCmBgYAoKIyMjIEFsaWduaW5nIHNlcSB0YWJsZQoKYGBge3J9CnNlcXRhYiA9IGNvbWJpbmVkQG90dV90YWJsZQoKc2VxdGFiX21lcmdlZCA9IGRhZGEyOjpjb2xsYXBzZU5vTWlzbWF0Y2goc2VxdGFiKQoKY29tYmluZWQgPSBwaHlsb3NlcShvdHVfdGFibGUoc2VxdGFiX21lcmdlZCwgdGF4YV9hcmVfcm93cz1GQUxTRSksCiAgICAgICAgICAgICAgIHNhbXBsZV9kYXRhKGNvbWJpbmVkQHNhbV9kYXRhKSwgCiAgICAgICAgICAgICAgIHRheF90YWJsZShjb21iaW5lZEB0YXhfdGFibGUpKQoKY29tYmluZWQKCiNzYW1wbGVfZGF0YShjb21iaW5lZCkKYGBgCgojIyBEYXRhIHRyYW5zZm9ybQoKYGBge3J9Cm90dV9jbHIgPSBhYnVuZGFuY2VzKGNvbWJpbmVkLCAiY2xyIikgJT4lIAogIHQoKQoKY29tYmluZWQgPSBwaHlsb3NlcShvdHVfdGFibGUob3R1X2NsciwgdGF4YV9hcmVfcm93cyA9IEZBTFNFKSwKICAgICAgICAgICAgICAgICAgIHNhbXBsZV9kYXRhKGNvbWJpbmVkQHNhbV9kYXRhKSwKICAgICAgICAgICAgICAgICAgIHRheF90YWJsZShjb21iaW5lZEB0YXhfdGFibGUpKQoKcm0ob3R1X2NscikKYGBgCgojIyBGZWF0dXJlIHRhYmxlCgojIyMgRXh0cmFjdCBkYXRhCgpPcmRlciBvZiBhc3YgY29sbmFtZXMgaW4gT1RVIHRhYmxlIGFuZCB0YXggdGFibGUgaXMgdGhlIHNhbWUuCmBgYHtyfQpmZWF0dXJlcyA9IGNvbWJpbmVkQG90dV90YWJsZSAlPiUKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgYXNfdGliYmxlKHJvd25hbWVzID0gTkEpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigic2FtcGxlIikKCmZlYXR1cmVfbGFiZWxzID0gYygic2FtcGxlIikKCmZlYXR1cmVzCmBgYAojIyMgWmVybyB2YXJpYW5jZSBmZWF0dXJlcwoKTm8gZmVhdHVyZXMgaGF2ZSB6ZXJvIHZhcmlhbmNlCmBgYHtyfQpuenYgPSBmZWF0dXJlcyAlPiUKICBzZWxlY3QoIXNhbXBsZSkgJT4lCiAgbmVhclplcm9WYXIoc2F2ZU1ldHJpY3MgPSBUUlVFKQpgYGAKCmBgYHtyfQpuenYgJT4lCiAgZmlsdGVyKG56diA9PSBUUlVFKQpgYGAKCgpgYGB7cn0Kcm0obnp2KQpgYGAKCiMjIyBDb3JyZWxhdGVkIHByZWRpY3RvcnMKCmBgYHtyfQpmZWF0dXJlX2NvciA9IGZlYXR1cmVzICU+JQogc2VsZWN0KCFzYW1wbGUpICU+JQogY29yKCkgCgpzdW1tYXJ5KGZlYXR1cmVfY29yW3VwcGVyLnRyaShmZWF0dXJlX2NvcildKQpgYGAKCmBgYHtyfQpoaWdoX2NvciA9IGZpbmRDb3JyZWxhdGlvbihmZWF0dXJlX2NvciwgY3V0b2ZmID0gLjUwKSAjdXNpbmcgYSBtb3JlIHBlcm1pc3NpdmUgY3V0b2ZmIGJlY2F1c2UgYSBsb3Qgb2YgY29sdW1ucyBzdGlsbCBsZWZ0IGluIHRoZSBpbnB1dCB0YWJsZQoKZmlsdGVyZWRfZmVhdHVyZXMgPSBmZWF0dXJlc1ssLWhpZ2hfY29yXQpgYGAKCmBgYHtyfQpybShoaWdoX2NvcikKcm0oZmVhdHVyZV9jb3IpCmBgYAoKIyMjIExpbmVhciBkZXBlbmRlbmNpZXMKCmBgYHtyfQpmZWF0dXJlX2xkID0gZmlsdGVyZWRfZmVhdHVyZXMgJT4lCiAgc2VsZWN0KCFzYW1wbGUpICU+JQogIGZpbmRMaW5lYXJDb21ib3MoKQoKZmlsdGVyZWRfZmVhdHVyZXMgPSBmaWx0ZXJlZF9mZWF0dXJlc1ssIC0gZmVhdHVyZV9sZCRyZW1vdmVdCmBgYAoKIyMgSW5wdXQgdGFibGUKYGBge3J9CnNhbXBsZV9sYWJlbHMgPSBjb21iaW5lZEBzYW1fZGF0YSU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBhc190aWJibGUocm93bmFtZXMgPSBOQSkgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJzYW1wbGUiKSAlPiUKICBzZWxlY3Qoc2FtcGxlLCBncm91cCkgJT4lCiAgbGVmdF9qb2luKGZpbHRlcmVkX2ZlYXR1cmVzKQoKaW5wdXQgPSBzYW1wbGVfbGFiZWxzICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KHNhbXBsZSwgIkFIQSIpKSAlPiUKICBtdXRhdGUoZ3JvdXBfc2h1ZmZsZSA9IHNhbXBsZShncm91cCkpCgpleHRlcm5hbF90ZXN0ID0gc2FtcGxlX2xhYmVscyAlPiUKICBmaWx0ZXIoIXN0cl9kZXRlY3Qoc2FtcGxlLCAiQUhBIikpICU+JQogIHNlbGVjdCgtYyhncm91cCkpCmBgYAoKIyBNb2RlbGluZwoKIyMgQmFzZWxpbmUgcGVyZm9ybWFuY2UKCmBgYHtyfQpsYWJlbF9saXN0ID0gYygiZ3JvdXAiLCAiZ3JvdXBfc2h1ZmZsZSIpCnJlc3VsdHMgPSBsb29wX2xhYmVsKGl0ZXJhdGlvbnMgPSAyMCwgaW5wdXQsIGxhYmVsX2xpc3QsIHBlcmNlbnQgPSAwLjgpCgphdWNfZGYgPSByZXN1bHRzW1syXV0KCmltcG9ydGFuY2VfZGYgPSByZXN1bHRzW1sxXV0KYGBgCgojIyBMb29wCgpgYGB7cn0KbGFiZWwgPSAiZ3JvdXAiCgppbXBvcnRhbmNlX2RmID0gZGF0YS5mcmFtZSgpCgpjdW1tdWxhdGl2ZV9leHRlcm5hbCA9IGRhdGEuZnJhbWUoKQoKYXVjX2xpc3QgPSBjKCkKCmN1cnJlbnRfZGF0YSA9IGlucHV0ICU+JQogICAgICBzZWxlY3QoLWxhYmVsX2xpc3RbbGFiZWxfbGlzdCAhPSBsYWJlbF0pICU+JQogICAgICBzZWxlY3QoLXNhbXBsZSkKCmZvcihpIGluIDE6MjApIHsKICAjIERhdGEgc3BsaXR0aW5nCiAgICB0cmFpbl9pbmQgPSBjYXJldDo6Y3JlYXRlRGF0YVBhcnRpdGlvbigoY3VycmVudF9kYXRhICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVsbChsYWJlbCkpLCAjc3BsaXQgYmFzZWQgb24gbGFiZWwgY29sdW1uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwID0gMC44LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZXMgPSAxKQogICAgdHJhaW4gPSBjdXJyZW50X2RhdGFbdHJhaW5faW5kLF0KICAgIHRlc3QgPSBjdXJyZW50X2RhdGFbLXRyYWluX2luZCxdCiAgICAKICAgICMgVHJhaW5pbmcgbW9kZWwKICAgIAogICAgZm9ybXVsYSA9IGxhYmVsICU+JQogICAgICBwYXN0ZTAoIn4uIikgJT4lCiAgICAgIGFzLmZvcm11bGEoKQogICAgCiAgICBmaXRfY29udHJvbCA9IGNhcmV0Ojp0cmFpbkNvbnRyb2wobWV0aG9kID0gIkNWIiwgIyA1LWZvbGQgY3Jvc3MgdmFsaWRhdGlvbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBudW1iZXIgPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJ5RnVuY3Rpb24gPSBwclN1bW1hcnksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzUHJvYnMgPSBUKQogICAgCiAgICBtb2RlbCA9IGNhcmV0Ojp0cmFpbihmb3JtdWxhLCAjdXNpbmcgZm9ybXVsYSBzcGVjaWZpZWQgCiAgICAgICAgICAgICAgZGF0YSA9IHRyYWluLAogICAgICAgICAgICAgICAgICBtZXRob2QgPSAicmYiLAogICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBmaXRfY29udHJvbCwKICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgbWV0cmljID0gIkFVQyIpCiAgICAKICAgICMgTW9kZWwgcHJlZGljdGlvbnMKICAgIAogICAgcHJlZGljdGlvbnMgPSBwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0gdGVzdCwgdHlwZT0gInByb2IiKQogICAgCiAgICBwcmVkaWN0aW9ucyRvYnMgPSB0ZXN0ICU+JQogICAgICBwdWxsKGxhYmVsKSAlPiUKICAgICAgYXMuZmFjdG9yKCkgCiAgICAKICAgICMgRXZhbHVhdGUgbW9kZWwKICAgIAogICAgZXZhbCA9IE1MZXZhbDo6ZXZhbG0ocHJlZGljdGlvbnMsIHNob3dwbG90cyA9IEZBTFNFLCBzaWxlbnQgPSBUUlVFKQogICAgCiAgICBhdWMgPSBldmFsJHN0ZHJlcyRgR3JvdXAxYFsxMywxXQogICAgCiAgICBhdWNfbGlzdCA9IGFwcGVuZChhdWNfbGlzdCwgYXVjKQogICAgCiAgICAjRXh0cmFjdCBmZWF0dXJlIGltcG9ydGFuY2UKCiAgICBmZWF0dXJlX2ltcG9ydGFuY2UgPSBjYXJldDo6dmFySW1wKG1vZGVsJGZpbmFsTW9kZWwpICU+JSAjIEJlbiBjYWxscyB0aGlzIGltcG9ydGFuY2VzIGluc3RlYWQgb2YgZmluYWwgbW9kZWwgYnV0IHRoZSByYW5raW5ncyBhcmUgZXhhY3RseSB0aGUgc2FtZSwgb25seSBudW1iZXIgdmFsdWUgc2NhbGUgaXMgZGlmZmVyZW50LgogICAgICBhcnJhbmdlKGRlc2MoT3ZlcmFsbCkpICU+JQogICAgICBhcy5tYXRyaXgoKSAlPiUKICAgICAgdCgpICU+JQogICAgICBhcy5kYXRhLmZyYW1lKCkKICAgIAogICAgZmVhdHVyZV9pbXBvcnRhbmNlJGF1YyA9IGF1YwogICAgCiAgICBmZWF0dXJlX2ltcG9ydGFuY2UkaXRlcmF0aW9uID0gaQogICAgCiAgICBmZWF0dXJlX2ltcG9ydGFuY2UkbGFiZWwgPSBsYWJlbAogICAgCiAgICBpbXBvcnRhbmNlX2RmID0gcmJpbmQoaW1wb3J0YW5jZV9kZiwgZmVhdHVyZV9pbXBvcnRhbmNlKQogICAgCiAgICAjUHJlZGljdGluZyBvbiBleHRlcm5hbCBkYXRhc2V0CiAgICAKICAgIGV4dGVybmFsX3ByZWRpY3Rpb25zID0gIHByZWRpY3QobW9kZWwsIG5ld2RhdGEgPSBleHRlcm5hbF90ZXN0LCB0eXBlPSAicHJvYiIpCiAgICAKICAgIGV4dGVybmFsX3ByZWRpY3Rpb25zJHNhbXBsZSA9IGV4dGVybmFsX3Rlc3Qkc2FtcGxlCiAgICAKICAgIGV4dGVybmFsX3ByZWRpY3Rpb25zJGF1YyA9IGF1YwogICAgCiAgICBleHRlcm5hbF9wcmVkaWN0aW9ucyRwcmVkID0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IGV4dGVybmFsX3Rlc3QpJT4lCiAgICAgIGFzLmZhY3RvciAoKQogICAgCiAgICBleHRlcm5hbF9wcmVkaWN0aW9ucyRpdGVyYXRpb24gPSBpCiAgICAKICAgIGN1bW11bGF0aXZlX2V4dGVybmFsID0gcmJpbmQoY3VtbXVsYXRpdmVfZXh0ZXJuYWwsIGV4dGVybmFsX3ByZWRpY3Rpb25zKQogICAgCn0KCmF1Y19saXN0CgppbXBvcnRhbmNlX2RmCgpjdW1tdWxhdGl2ZV9leHRlcm5hbCAgCiAgCmBgYAoKIyBQbG90dGluZwoKIyMgQVVDIHBsb3QgYW5kIHQtdGVzdApgYGB7cn0KZGF0YS5mcmFtZShhdWMgPSBhdWNfbGlzdCklPiUKICBnZ3Bsb3QoKSsKICBnZW9tX2JveHBsb3QoYWVzKHkgPSBhdWMpKSsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgojIyBTYXZlIGV4dGVybmFsIHByZWRpY3Rpb25zCgojIyMgcHJlZGljdGlvbnMKYGBge3J9Cm1heCA9IG1heChhdWNfbGlzdCkKCnNhbWRmID0gY19wc0BzYW1fZGF0YSU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICBhc190aWJibGUocm93bmFtZXMgPSBOQSkgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJzYW1wbGUiKSAlPiUKICBzZWxlY3Qoc2FtcGxlLCBpZCwgdHJlYXRtZW50LCB0cnVlX3dlZWssIHRpbWVwb2ludCkgCgpzYXZlID0gY3VtbXVsYXRpdmVfZXh0ZXJuYWwgJT4lCiAgI2ZpbHRlcihhdWMgPT0gbWF4KSAlPiUKICBsZWZ0X2pvaW4oc2FtZGYpICU+JQogIGFycmFuZ2UoaXRlcmF0aW9uLCB0cmVhdG1lbnQsIGlkLCB0cnVlX3dlZWspCmBgYAoKYGBge3J9CndyaXRlX2NzdihzYXZlLCAiL1VzZXJzL2FhMzcwL0xpYnJhcnkvQ2xvdWRTdG9yYWdlL0JveC1Cb3gvcHJvamVjdF9kYXZpZGxhYi9MQURfTEFCX1BlcnNvbm5lbC9BbW1hcmFfQS9Qcm9qZWN0cy9NYWNoaW5lX2xlYXJuaW5nL0xBRF9sYWJfTUwvY29kZS9haGFfdG9fY2hvaWNlX3ByZWRpY3Rpb25zLmNzdiIpCmBgYAoKCiMjIyB2YXJpYWJsZSBpbXBvcnRhbmNlcwoKYGBge3J9CndyaXRlX2NzdihpbXBvcnRhbmNlX2RmLCAiL1VzZXJzL2FhMzcwL0xpYnJhcnkvQ2xvdWRTdG9yYWdlL0JveC1Cb3gvcHJvamVjdF9kYXZpZGxhYi9MQURfTEFCX1BlcnNvbm5lbC9BbW1hcmFfQS9Qcm9qZWN0cy9NYWNoaW5lX2xlYXJuaW5nL0xBRF9sYWJfTUwvY29kZS9haGFfdG9fY2hvaWNlX2ZlYXR1cmVfaW1wb3J0YW5jZS5jc3YiKQpgYGAKCgoKCgoK